package com.acmedcare.framework.exchange.center.executer.job;

import com.acmedcare.framework.exchange.center.core.resolve.PathResolveFactory;
import com.acmedcare.framework.exchange.center.entity.bean.Task;
import com.acmedcare.framework.exchange.center.entity.bean.TaskDetail;
import com.acmedcare.framework.exchange.center.entity.dto.TaskFailStatus;
import com.acmedcare.framework.exchange.center.executer.ExecutorConfig;
import com.acmedcare.framework.exchange.center.executer.TaskRunnerManager;
import com.acmedcare.framework.kits.StringUtils;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Job;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

@SuppressWarnings("unused")
@Slf4j
public class NormalExecuteJob extends BaseJob implements Job {

  private final String bank = " ";
  private static LoadingCache<Long, String> versionCache;
  private static LoadingCache<Long, String> pathCache;

  public NormalExecuteJob() {

    if (pathCache == null) {

      pathCache = CacheBuilder.newBuilder()
          .expireAfterWrite(30, TimeUnit.MINUTES)
          .initialCapacity(10)
          .maximumSize(1000)
          .build(new CacheLoader<Long, String>() {
            @Override
            public String load(Long a) {
              return "";
            }
          });
    }

    if (versionCache == null) {

      versionCache = CacheBuilder.newBuilder()
          .expireAfterWrite(5, TimeUnit.HOURS)
          .initialCapacity(10)
          .maximumSize(1000)
          .build(new CacheLoader<Long, String>() {
            @Override
            public String load(Long a) {
              return "";
            }
          });
    }
  }

  @Override
  public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {

    paths.clear();
    JobDetail detail = jobExecutionContext.getJobDetail();
    Task task = (Task) detail.getJobDataMap().get(ExecutorConfig.TASKINFO);
    TaskRunnerManager manager = new TaskRunnerManager();

    //如果存在正在执行的任务，直接跳过执行
    Boolean running = manager.isRun(task);

    TaskDetail taskDetail = new TaskDetail();
    taskDetail.setExecutorName(ExecutorConfig.getInstance().getExecutorName());
    try {

      task.setLastRunTime(new Date());
      publishTask(task);
      taskDetail.setName(task.getName());
      taskDetail.setDescription(task.getDescription());
      taskDetail.setStatus(false);
      taskDetail.setExecuteTime(new Date());
      if (!running) {

        try {

          //注册运行器
          manager.register(task);
          String shellScript = createNormalCommand(task);
          long startTime = System.currentTimeMillis();   //获取开始时间
          executeShell(shellScript, taskDetail, task.getTimeOut());
          long endTime = System.currentTimeMillis(); //获取结束时间
          taskDetail.setDuringTime(endTime - startTime);
        } catch (Exception e) {

          throw e;
        } finally {

          //卸载运行器
          manager.unRegister(task);
        }
      } else {

        taskDetail.setDuringTime(Long.valueOf(0));
        taskDetail.setFailStatus(TaskFailStatus.SKIP);
        taskDetail.setExecuteLog("任务正在执行，已经跳过执行!");
        taskDetail.setStatus(false);
      }
    } catch (Exception e) {

      String msg = e.getMessage() + e.getStackTrace();
      if (taskDetail.getExecuteLog() != null) {
        msg = taskDetail.getExecuteLog() + msg;
      }

      taskDetail.setExecuteLog(msg);
      taskDetail.setFailStatus(TaskFailStatus.ERROR);
      throw new JobExecutionException("task convert fail", e);
    } finally {

      clearJar();
      publishDetail(taskDetail);
    }
  }

  public String createNormalCommand(Task task) throws Exception {

    StringBuilder builder = new StringBuilder();

    builder.append("java");
    builder.append(bank);
    // jvm memory
    builder.append(
        "-server -Xms28m -Xmx128m -Xmn28m -XX:MetaspaceSize=28m -XX:MaxMetaspaceSize=256m");
    builder.append(bank);
    // jvm gc
    builder.append(
        "-XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:CMSInitiatingOccupancyFraction=70 -XX:+CMSParallelRemarkEnabled -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+CMSClassUnloadingEnabled -XX:SurvivorRatio=8  -XX:-UseParNewGC");
    //设置dump文件路径
    builder.append(bank);

    //打印gc并且在crash之后设置dump文件
    String tempPathName = ExecutorConfig.getInstance().getFileRootPath() + "/" + task.getName();
    builder.append(String.format(
        "-verbose:gc -Xloggc:%s_gc.log -XX:HeapDumpPath=%s/HeapDumpOnOutOfMemoryError/ -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintAdaptiveSizePolicy"
        , tempPathName, tempPathName));

    builder.append(bank);
    builder.append("-classpath");
    builder.append(bank);

    String version = versionCache.get(task.getTaskId());

    String path = null;
    // 如果当前版本存在则直接获取文件路径
    if (StringUtils.isNotEmpty(version)
        && task.getVersion().equals(version) && StringUtils
        .isNotEmpty(pathCache.get(task.getTaskId()))) {
      path = pathCache.get(task.getTaskId());

      //检查路径下是否有文件,如果不存在则去下载文件
      if (!Files.exists(Paths.get(path))) {
        path = null;
      }
    }

    if (path == null) {

      if (StringUtils.isEmpty(task.getJarName())) {
        path = new PathResolveFactory(ExecutorConfig.getInstance().getHdfs())
            .getPathResolve(task.getExecuteJar(), ExecutorConfig.getInstance().getFileRootPath())
            .resolve();
      } else {
        path = new PathResolveFactory(ExecutorConfig.getInstance().getHdfs()).
            getPathResolve(task.getExecuteJar(), task.getJarName(),
                ExecutorConfig.getInstance().getFileRootPath())
            .resolve();
      }

      versionCache.put(task.getTaskId(), task.getVersion());
      pathCache.put(task.getTaskId(), path);
    }

    builder.append(path);
    builder.append(bank);
    builder.append(task.getExecuteMainClass());
    return builder.toString();
  }
}
